﻿using Novacode;
using QuanLyNganHangDe.DAL;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MarkDocuments
{
    public partial class FormDoTagging : Form
    {
        private List<TagTask> m_TTaskList;

        #region CONSTANTS
        private static string SEP = " ( \r\n\t";
        private static string KHOI = "KHỐI ";
        private static string KHOI_CHARS = "KHỐI 0123456789.:";
        private static string CAU = "CÂU ";
        private static string CAU_CHARS = "CÂU 0123456789.:";
        private static string PHAN_DAP_AN = "II. PHẦN ĐÁP ÁN";
        private static string NEWLINE = " \n";
        #endregion

        public FormDoTagging(List<TagTask> lst)
        {
            InitializeComponent();
            UpdateGuiByOutputFolderModeChanged();
            lblProgress.Text = "";
            m_TTaskList = lst;
        }

        private void lnkSelectOutputFolder_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var dlgFolder = new FolderBrowserDialog();
            if (dlgFolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                tbxOutputFolder.Text = dlgFolder.SelectedPath;
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void radWriteOutputAtTheSameInputFolder_CheckedChanged(object sender, EventArgs e)
        {
            UpdateGuiByOutputFolderModeChanged();
        }

        private void UpdateGuiByOutputFolderModeChanged()
        {
            if (radWriteOutputAtCustomFolder.Checked)
            {
                tbxOutputFolder.Enabled = true;
                lnkSelectOutputFolder.Enabled = true;
            }
            else
            {
                tbxOutputFolder.Enabled = false;
                lnkSelectOutputFolder.Enabled = false;
            }
        }

        /// <summary>
        /// đoạn văn bản có phải bắt đầu 1 khối?
        /// </summary>
        /// <param name="stText">chuỗi đã được in hoa, xoá các ký tự trắng thừa (\r \n \space) ở đầu</param>
        /// <returns></returns>
        private static bool laBatDauKhoi(string stText)
        {
            stText = stText.Trim('\t', ' ');
            if (stText.StartsWith(KHOI) == false)
                return false;
            else
            {
                var idxNextSpace = -1;
                for (int j = KHOI.Length - 1; j < stText.Length; j++)
                    if (SEP.Contains(stText[j]))
                    {
                        idxNextSpace = j;
                        break;
                    }
                if (idxNextSpace < 0)
                    return true;
                else
                {
                    stText = stText.Substring(0, idxNextSpace);
                    for (int j = 0; j < stText.Length; j++)
                        if (KHOI_CHARS.Contains(stText[j]) == false)
                            return false;
                    return true;
                }
            }
        }

        /// <summary>
        /// đoạn văn bản có phải bắt đầu 1 câu?
        /// </summary>
        /// <param name="stText">chuỗi đã được in hoa, xoá các ký tự trắng thừa (\r \n \space) ở đầu</param>
        /// <returns></returns>
        private static bool laBatDauCau(string stText)
        {
            stText = stText.Trim('\t', ' ');
            if (stText.StartsWith(CAU) == false)
                return false;
            else
            {
                var idxNextSpace = -1;
                for (int j = CAU.Length - 1; j < stText.Length; j++)
                    if (SEP.Contains(stText[j]))
                    {
                        idxNextSpace = j;
                        break;
                    }
                if (idxNextSpace < 0)
                    return true;
                else
                {
                    stText = stText.Substring(0, idxNextSpace);
                    for (int j = 0; j < stText.Length; j++)
                        if (CAU_CHARS.Contains(stText[j]) == false)
                            return false;
                    return true;
                }
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (btnStart.Tag != null)
            {
                var bw2 = btnStart.Tag as BackgroundWorker;
                bw2.CancelAsync();
                btnStart.Enabled = false;
                return;
            }

            var outputCungThuMucInput = radWriteOutputAtTheSameInputFolder.Checked;
            var outputDir = tbxOutputFolder.Text.Trim();
            if (outputCungThuMucInput == false && (string.IsNullOrEmpty(outputDir) || System.IO.Directory.Exists(outputDir) == false))
            {
                MessageBox.Show("Đường dẫn thư mục chứa tệp đánh dấu là không hợp lệ!");
                return;
            }

            // Reset trạng thái của tất cả các tệp
            foreach (var ttask in m_TTaskList)
            {
                ttask.SetNotTaggedYet();

                var fiInputFile = new System.IO.FileInfo(ttask.InputFilename);
                var name = System.IO.Path.GetFileNameWithoutExtension(fiInputFile.Name);
                var ext = System.IO.Path.GetExtension(fiInputFile.Name);
                var outputFileName = string.Format("{0}-TAG{1}", name, ext);
                if (outputCungThuMucInput)
                    ttask.OutputFilename = System.IO.Path.Combine(fiInputFile.Directory.FullName, outputFileName);
                else
                    ttask.OutputFilename = System.IO.Path.Combine(outputDir, outputFileName);
            }

            // Thực hiện đánh thẻ
            var bw = new BackgroundWorker();
            bw.WorkerSupportsCancellation = true;
            bw.WorkerReportsProgress = true;
            bw.DoWork += (oSender, oE) =>
            {
                #region DoWork
                var nOk = 0;
                var nOkWithWarning = 0;
                var nError = 0;
                for (int idx = 0; idx < m_TTaskList.Count; idx++)
                {
                    if (bw.CancellationPending)
                        break;

                    var x = m_TTaskList[idx];
                    var status = string.Format("{0}/{1}: {2}", idx + 1, m_TTaskList.Count, x.InputFilename);
                    bw.ReportProgress(0, status);

                    try
                    {
                        #region Convert x
                        var lstExc = new List<Exception>();
                        var stFilename = x.InputFilename;
                        if (System.IO.File.Exists(stFilename) == false)
                            throw new Exception("Tệp đầu vào không tồn tại");
                        var stFilename2 = x.OutputFilename;
                        var option = Program.TagOptions;

                        using (var document = DocX.Load(stFilename))
                        {
                            var documentParagraphs = document.Paragraphs;
                            var nParaCount = documentParagraphs.Count;

                            #region var lstParCauHoi = danh sách các par câu hỏi;
                            var lstParCauHoi = new List<Paragraph>();
                            var idxEndOfCauHoiPar = -1;
                            for (int i = 0; i < nParaCount; i++)
                            {
                                var par = documentParagraphs[i];
                                var parContent = par.Text.Trim('\r', '\n', '\t', ' ').ToUpper();
                                if (parContent != PHAN_DAP_AN)
                                {
                                    if (laBatDauCau(parContent) || laBatDauKhoi(parContent))
                                        lstParCauHoi.Add(par);
                                }
                                else
                                {
                                    idxEndOfCauHoiPar = i;
                                    break;
                                }
                            }
                            #endregion

                            #region var lstParDapAn = danh sách các par đáp án
                            var lstParDapAn = new List<Paragraph>();
                            if (idxEndOfCauHoiPar >= 0)
                                for (int i = idxEndOfCauHoiPar; i < nParaCount; i++)
                                {
                                    lstParDapAn.Add(documentParagraphs[i]);
                                }

                            // Loại bỏ các đoạn rỗng (nếu có) ở phần cuối cùng
                            var idxLast = lstParDapAn.Count - 1;
                            if (idxLast >= 0)
                                while (true)
                                {
                                    var par = lstParDapAn[idxLast];
                                    var parContent = par.Text.Trim(' ', '\t', '\n', '\r');
                                    if (parContent == "")
                                    {
                                        lstParDapAn.RemoveAt(idxLast);
                                        idxLast = lstParDapAn.Count - 1;
                                        if (idxLast < 0)
                                            break;
                                    }
                                    else
                                        break;
                                }
                            #endregion

                            #region Bổ sung các thẻ cho các khối câu hỏi, câu hỏi
                            var nParCauHoi = lstParCauHoi.Count;
                            var laKhoiCauHoiDauTien = true;
                            var laCauHoiDauTienCuaKhoi = true;
                            for (int i = 0; i < lstParCauHoi.Count; i++)
                            {
                                var par = lstParCauHoi[i];
                                var parContent = par.Text.TrimStart('\t', '\r', '\n', ' ').ToUpper();

                                var batDauKhoi = laBatDauKhoi(parContent);
                                if (batDauKhoi)
                                {
                                    #region Bắt đầu 1 khối..
                                    laCauHoiDauTienCuaKhoi = true;
                                    if (laKhoiCauHoiDauTien)
                                    {
                                        #region là khối câu hỏi đầu tiên => [lq_start]\[part_start]
                                        var textToInsert = option.PhanCauHoi_Begin + NEWLINE + option.KhoiCauHoi_Begin;
                                        try
                                        {
                                            par.InsertTextBeforeFirstVisibleChar(textToInsert);
                                        }
                                        catch (Exception exc)
                                        {
                                            lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho khối câu hỏi đầu tiên [{0}]", par.Text), exc));
                                        }
                                        laKhoiCauHoiDauTien = false;
                                        #endregion
                                    }
                                    else
                                    {
                                        #region là khối câu hỏi thứ 2, 3, ... => [question_end][part_end]\[part_start]
                                        try
                                        {
                                            par.InsertTextBeforeFirstVisibleChar(option.CauHoi_End + NEWLINE + option.KhoiCauHoi_End + NEWLINE + option.KhoiCauHoi_Begin);
                                        }
                                        catch (Exception exc)
                                        {
                                            lstExc.Add(new Exception(string.Format("Thêm thẻ cho khối câu hỏi [{0}]", par.Text), exc));
                                        }
                                        #endregion
                                    }
                                    #endregion
                                }
                                else
                                {
                                    #region Bắt đầu 1 câu hỏi?
                                    var batDauCauHoi = laBatDauCau(parContent);

                                    if (batDauCauHoi)
                                    {
                                        if (laCauHoiDauTienCuaKhoi)
                                        {
                                            #region câu hỏi đầu tiên của khối => [question_start]
                                            try
                                            {
                                                par.InsertTextBeforeFirstVisibleChar(option.CauHoi_Begin);
                                            }
                                            catch (Exception exc)
                                            {
                                                lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho câu hỏi đầu tiên của khối [{0}]", par.Text), exc));
                                            }
                                            laCauHoiDauTienCuaKhoi = false;
                                            #endregion
                                        }
                                        else
                                        {
                                            #region câu hỏi thứ 2, 3, ... của khối => [question_end]\[question_start]
                                            try
                                            {
                                                par.InsertTextBeforeFirstVisibleChar(option.CauHoi_End + NEWLINE + option.CauHoi_Begin);
                                            }
                                            catch (Exception exc)
                                            {
                                                lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ cho câu hỏi trong khối [{0}]", par.Text), exc));
                                            }
                                            #endregion
                                        }
                                    }
                                    #endregion
                                }
                            }
                            #endregion

                            #region Đóng câu hỏi cuối cùng, đóng khối câu hỏi cuối cùng và đóng phần câu hỏi [question_end]\[part_end]\[lq_end]
                            //var lastQuestionPar = lstParCauHoi[lstParCauHoi.Count - 1];
                            //lastQuestionPar.AppendLine(option.CauHoi_End);
                            //lastQuestionPar.AppendLine(option.KhoiCauHoi_End);
                            //lastQuestionPar.AppendLine(option.PhanCauHoi_End);
                            #endregion

                            #region Bổ sung các thẻ cho các khối trả lời, câu trả lời
                            var laDapAnDauTienCuaKhoi = true;
                            var laKhoiDapAnDauTien = true;
                            int soLuongDapAn = 0;
                            var nParDapAn = lstParDapAn.Count;
                            for (int i = 0; i < lstParDapAn.Count; i++)
                            {
                                var par = lstParDapAn[i];
                                var parContent = par.Text.Trim(' ', '\t', '\r', '\n').ToUpper();
                                if (parContent == PHAN_DAP_AN)
                                {
                                    #region Bắt đầu phần đáp án => Bổ sung [lanw_start] truước II. PHẦN ĐÁP ÁN
                                    try
                                    {
                                        // Đóng câu hỏi cuối cùng
                                        par.InsertTextBeforeFirstVisibleChar(string.Format("{0}\r\n{1}\r\n{2}\r\n", option.CauHoi_End, option.KhoiCauHoi_End, option.PhanCauHoi_End));
                                        
                                        // Bắt đầu phần đáp án
                                        //par.InsertTextBeforeFirstVisibleChar(option.PhanDapAn_Begin);
                                        par.AppendLine(option.PhanDapAn_Begin);
                                    }
                                    catch (Exception exc)
                                    {
                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ bắt đầu phần đáp án cho [{0}]", par.Text), exc));
                                    }
                                    #endregion
                                }
                                else
                                {
                                    var batDauKhoi = laBatDauKhoi(parContent); //parcontent.StartsWith("KHỐI ")
                                    if (batDauKhoi)
                                    {
                                        laDapAnDauTienCuaKhoi = true;
                                        #region Bắt đầu khối đáp án => bổ sung kết thúc đáp án ([anw_end]) cuối cùng của khối trước đó (nếu có)
                                        if (laKhoiDapAnDauTien == false)
                                        {
                                            var idxPrevAnswer = i - 3;
                                            if (idxPrevAnswer >= 0 && soLuongDapAn > 0)
                                            {
                                                var parPrevAnswer = lstParDapAn[idxPrevAnswer];
                                                try
                                                {
                                                    parPrevAnswer.Append(option.DapAn_End);
                                                }
                                                catch (Exception exc)
                                                {
                                                    lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ kết thúc cho đáp án trước khối [{0}]", par.Text), exc));
                                                }
                                            }
                                        }
                                        laKhoiDapAnDauTien = false;
                                        #endregion
                                    }
                                    else
                                    {
                                        var batDauCau = laBatDauCau(parContent); //parcontent.StartsWith(CAU)
                                        if (batDauCau)
                                        {
                                            #region Bắt đầu 1 đáp án cho câu hỏi => [anw_start]
                                            try
                                            {
                                                par.InsertTextBeforeFirstVisibleChar(option.DapAn_Begin);
                                            }
                                            catch (Exception exc)
                                            {
                                                lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ bắt đầu đáp án cho câu [{0}]", par.Text), exc));
                                            }
                                            #endregion

                                            soLuongDapAn++;

                                            if (laDapAnDauTienCuaKhoi == false)
                                            {
                                                #region Không phải là đáp án đầu tiên => Thêm phần kết thúc đáp án [anw_end] của câu trước đó
                                                var idxPrevParagraph = i - 1;
                                                if (0 <= idxPrevParagraph && idxPrevParagraph <= lstParDapAn.Count - 1)
                                                {
                                                    try
                                                    {
                                                        var prevPara = lstParDapAn[idxPrevParagraph];
                                                        prevPara.Append(option.DapAn_End);
                                                    }
                                                    catch (Exception exc)
                                                    {
                                                        lstExc.Add(new Exception(string.Format("Lỗi khi thêm thẻ kết thúc đáp án cho câu hỏi [{0}]", par.Text), exc));
                                                    }
                                                }
                                                #endregion
                                            }
                                            laDapAnDauTienCuaKhoi = false;
                                        }
                                    }
                                }
                            }
                            #endregion

                            #region Đóng đáp án cuối cùng => [anw_end]
                            // Ô tương ứng điểm số của đáp án cuối là par cách par cuối cùng 2 phần tử
                            var idxLastAnswerPar = lstParDapAn.Count - 3;
                            if (0 <= idxLastAnswerPar && idxLastAnswerPar <= lstParDapAn.Count - 1)
                            {
                                var lastAnswerPar = lstParDapAn[idxLastAnswerPar];
                                lastAnswerPar.Append(option.DapAn_End);
                            }
                            #endregion

                            #region Đóng phần đáp [lanw_end]
                            if (lstParDapAn.Count > 0)
                                document.Tables[document.Tables.Count - 1].InsertParagraphAfterSelf(option.PhanDapAn_End);
                            #endregion

                            document.SaveAs(stFilename2);
                        }
                        var sbTotalErrors = new StringBuilder();
                        for (int i = 0; i < lstExc.Count; i++)
                            sbTotalErrors.AppendFormat("{0}:{1}\r\n", lstExc[i].Message, lstExc[i].ToString());
                        var stTotalErrors = sbTotalErrors.ToString();
                        if (string.IsNullOrEmpty(stTotalErrors))
                        {
                            x.SetOK();
                            nOk++;
                        }
                        else
                        {
                            x.SetWarnings(lstExc.Count, stTotalErrors);
                            nOkWithWarning++;
                        }
                        #endregion
                    }
                    catch (Exception exc)
                    {
                        x.SetError(exc);
                        nError++;
                    }
                }

                oE.Result = string.Format("   - Số tệp thành công: {0}\n   - Số tệp có cảnh báo: {1}\n   - Số tệp lỗi: {2}",
                    nOk, nOkWithWarning, nError);
                #endregion
            };
            bw.ProgressChanged += (oSender, oE) =>
            {
                lblProgress.Text = oE.UserState.ToString();
            };
            bw.RunWorkerCompleted += (oSender, oE) =>
            {
                #region RunWorkerCompleted
                picBusy.Visible = false;
                lblProgress.Visible = false;
                btnStart.Enabled = true;
                btnStart.Text = "Bắt đầu";
                btnStart.Tag = null;
                btnClose.Enabled = true;

                if (bw.CancellationPending || oE.Cancelled)
                {
                    if (oE.Result == null)
                        MessageBox.Show("Đã huỷ ngang bởi người dùng");
                    else
                        MessageBox.Show("Đã huỷ ngang bởi người dùng. Kết quả:\n" + oE.Result.ToString());
                }
                else
                {
                    MessageBox.Show("Đã thực hiện chuyển đổi xong. Kết quả:\n" + oE.Result.ToString());
                }
                #endregion
            };

            btnClose.Enabled = false;
            btnStart.Tag = bw;
            btnStart.Text = "Dừng lại";
            picBusy.Visible = true;
            lblProgress.Text = "";
            lblProgress.Visible = true;
            bw.RunWorkerAsync();
        }

        private void FormDoTagging2_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (btnStart.Tag != null)
                e.Cancel = true;
        }
    }
}
